Skip to content

Validator cookie - #13

Merged
zancas merged 4 commits into
mainfrom
validator_cookie
Nov 9, 2026
Merged

zancas merged 4 commits into
mainfrom
validator_cookie

Conversation

@elicbarbieri

Copy link
Copy Markdown
Collaborator

Fixes:

Validator cookie auth that survives validator restarts, and finds a local validator's cookie with no config

  • Cookie read per request, not once at boot: a zebrad/zakurad restart's new cookie is used at once (was 401 until zingod restarted), and a cookie not yet written at boot is a retried request failure, never a failed start
  • No credentials + loopback jsonrpc_address = the validator's default cookie: /zebra/.cookie or /zakura/.cookie, whichever exists (neither = no auth; both = set cookie_path). A remote validator is never sent a local cookie
  • Verified against zebrad 7.0.0-rc.0 regtest with default cookie auth: zingod up before zebrad, serving once it starts, live again within a second of a zebrad restart

Stacked on #12; releases with it as 1.1.0 (one more minor change file). z3's explicit cookie_path over its shared volume gets the per-request read.

@elicbarbieri
elicbarbieri added this pull request to stack #14 November 9, 2026 19:07
Base automatically changed from fix_config_params to main November 9, 2026 19:14
… cookie found

A validator restart's new cookie is used at once; a missing cookie at boot is retried, not fatal.
No credentials + loopback validator = zebrad's or zakurad's default cookie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zancas and others added 3 commits November 9, 2026 12:22
…n refused

A review of the per-request cookie read found three defects, and each
now has a test that failed before this change.

Two default cookie files failed every request with an ambiguity error,
so a cookie left behind by one validator broke a loopback entry for the
other. The most recently written candidate is now sent, the earlier
candidate wins a tie, and the AmbiguousCookie error is removed.

A cookie that could never be read no longer failed the start, because
the read had moved from construction to the request. The client's
constructor reads the cookie once more: a file that exists and cannot be
read fails the start, and a file not yet written does not.

Every request read the cookie with blocking file I/O on the async
executor, and could send an empty token caught in the validator's
rewrite. The client now reads the cookie on the blocking pool at the
first request and holds it. A 401 or 403 drops the held credentials when
they are the ones that were refused, so the next request reads the file
again. An empty cookie is refused before any request is sent.

The decisions are pure functions over the files as read: which
credentials a set of readings yields, whether a reading refuses the
start, and what the client holds after a reply. The file reads sit in
two thin functions around them.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The credentials table keeps one row per distinct outcome; the start check keeps its two cases;
the client keeps the refused-then-read-again restart test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…refuses

A loopback address says where a socket connects, not who answers it, so
an entry with no credentials could hand the local validator's cookie to
a tunnel, a port-forward, or another user's listener. Such an entry now
starts with no credentials. The first 401 or 403 drops that state, and
only then does the next request read the default cookie and send it.
Nothing reads a default cookie at start.

The new Reach enum names where an address leads: Loopback, Private,
Public, or Named. It reads the host from the same URL parse that builds
the validator's URL, so the two can no longer disagree on forms such as
127.1 or a trailing dot. It replaces the hand-written loopback check and
the private-listen-address check in the zingod config, and the choice of
credentials becomes one match over it.

What the client holds is now the Held enum over a Credentials enum,
where it was an Option of an Option of a pair. Unread, no auth, and a
basic pair are three named states.

The config no longer special-cases an empty candidate list, so its test
stops depending on the machine's cache directory. The test validators
share one listener, one reply, and one request reader. The prose
doc-comments and the inline comment the change had added are removed,
and each touched expect states its invariant.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@zancas
zancas merged commit c0146ce into main Nov 9, 2026
3 checks passed
@zancas
zancas deleted the validator_cookie branch November 9, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants